Code
library(tidyverse)
library(sf)En este encuentro se buscará incorporar instrumentos más avanzados al momento de visualizar información geográfica.
El objetivo será analizar las elecciones en la Provincia de Buenos Aires a nivel de sección electoral. ¿Dónde le fue mejor y peor a cada partido? ¿Qué región contribuyó más a la fuerza ganadora?
Cargamos las librerías.
library(tidyverse)
library(sf)Primero hay que armar el objeto geográfico con resultados electorales.
res <- readxl::read_excel(params$ruta_pba)
dim(res)[1] 1080 10
geo <- st_read(params$ruta_partidos) %>%
mutate(id = paste0("BUENOS AIRES_",str_to_upper(nam)),
id = stringi::stri_trans_general(id, "Latin-ASCII"),
prov="PBA") Reading layer `partidos' from data source
`C:\Users\tomas.bustos\Documents\personal\estacion-r\Mapas en accion\geo\partidos.geojson'
using driver `GeoJSON'
Simple feature collection with 143 features and 8 fields
Geometry type: MULTIPOLYGON
Dimension: XY
Bounding box: xmin: -63.39 ymin: -41.04 xmax: -56.66 ymax: -33.26
Geodetic CRS: WGS 84
geo %>% ggplot()+geom_sf()Chequamos claves de unión.
geo_check <- geo %>% st_drop_geometry() %>% select(id) %>% distinct(id) %>% mutate(base_geo=1)
geo_check <- res %>%
select(id) %>%
distinct(id) %>%
mutate(base_res=1) %>%
full_join(geo_check, by="id") %>%
filter(is.na(base_res) | is.na(base_geo))
geo_check# A tibble: 18 × 3
id base_res base_geo
<chr> <dbl> <dbl>
1 BUENOS AIRES_25 DE MAYO 1 NA
2 BUENOS AIRES_9 DE JULIO 1 NA
3 BUENOS AIRES_CAÑUELAS 1 NA
4 BUENOS AIRES_CORONEL DE MARINA LEONARDO ROSALES 1 NA
5 BUENOS AIRES_GENERAL JUAN MADARIAGA 1 NA
6 BUENOS AIRES_NUEVE DE JULIO NA 1
7 BUENOS AIRES_CANUELAS NA 1
8 BUENOS AIRES_GENERAL MADARIAGA NA 1
9 BUENOS AIRES_VEINTICINCO DE MAYO NA 1
10 BUENOS AIRES_ISLAS BARADERO NA 1
11 BUENOS AIRES_ISLAS DE ZARATE NA 1
12 BUENOS AIRES_ISLAS SAN FERNANDO NA 1
13 BUENOS AIRES_ISLAS RAMALLO NA 1
14 BUENOS AIRES_ISLAS SAN PEDRO NA 1
15 BUENOS AIRES_ISLAS CAMPANA NA 1
16 BUENOS AIRES_CORONEL ROSALES NA 1
17 BUENOS AIRES_ISLAS TIGRE NA 1
18 BUENOS AIRES_ISLAS DE SAN NICOLAS NA 1
Reemplazamos y unimos.
geo <- geo %>%
mutate(id = str_replace(id, "BUENOS AIRES_NUEVE DE JULIO", "BUENOS AIRES_9 DE JULIO"),
id = str_replace(id, "BUENOS AIRES_CANUELAS", "BUENOS AIRES_CAÑUELAS"),
id = str_replace(id, "BUENOS AIRES_GENERAL MADARIAGA", "BUENOS AIRES_GENERAL JUAN MADARIAGA"),
id = str_replace(id, "BUENOS AIRES_VEINTICINCO DE MAYO", "BUENOS AIRES_25 DE MAYO")) %>%
select(id, cde, prov, geometry)
df <- res %>%
left_join(geo, by="id") %>%
mutate(Porcentaje = as.numeric(Porcentaje),
Votos = as.numeric(Votos)) %>%
st_as_sf()
head(df)Simple feature collection with 6 features and 12 fields
Geometry type: MULTIPOLYGON
Dimension: XY
Bounding box: xmin: -63.39 ymin: -38.28 xmax: -58.29 ymax: -34.75
Geodetic CRS: WGS 84
# A tibble: 6 × 13
id Seccion Elecciones Partido Porcentaje Votos Participacion Electores
<chr> <chr> <chr> <chr> <dbl> <dbl> <chr> <chr>
1 BUENOS AI… 25 De … GENERALES… BLANCO 3.69 925 79 31754
2 BUENOS AI… 9 De J… GENERALES… BLANCO 2.2 746 78.38 43208
3 BUENOS AI… Adolfo… GENERALES… BLANCO 3.39 361 73.49 14505
4 BUENOS AI… Adolfo… GENERALES… BLANCO 5.3 441 76.65 10861
5 BUENOS AI… Alberti GENERALES… BLANCO 3.68 292 82.21 9648
6 BUENOS AI… Almira… GENERALES… BLANCO 2.48 8821 77.96 455602
# ℹ 5 more variables: Votantes <chr>, Provincia <chr>, cde <chr>, prov <chr>,
# geometry <MULTIPOLYGON [°]>
Un problema común al trabajar con grandes territorios es el desequilibro entre importancia de la unidad y el tamaño de su área. A veces, territorios muy importantes no son visibles sólo por ser pequeños. Esto no implica que sean prescindibles: por su población, por su aporte económico o simplemente para no ocultar ningún dato, a veces es necesario hacer un pequeño hack para verlos en pantalla.
main_plot <- df %>%
filter(Partido == "UNION POR LA PATRIA") %>%
ggplot()+
geom_sf(aes(fill=Porcentaje), color="black")+
scale_fill_fermenter(palette = "Blues", direction = 1, n.breaks=5,
labels = scales::label_number(suffix = "%"))+
labs(x="", y="", fill="",
title="Unión por la Patria",
subtitle="Elecciones generales 2023",
caption="Elaboración propia según datos de datacp.ar")+
theme_void()+
theme(plot.caption = element_text(hjust = 0))
main_plotEl mapa anterior tiene una complicación: el conurbano bonaerense, que alberga dos tercios de la población de la provincia, casi no se aprecia en la figura. Es subsanable con la incorporación de minimapas. Un pequeño rodeo: la delimitación de lo que llamamos conurbano es una discusión con múltiples aristas y criterios. Aquí, sólo para evadirla prácticamente, tomaremos un recorte que habilita la división de secciones provinciales de la Provincia de Buenos Aires. Definiremos al conurbano como la primera y tercera sección provincial.
equi <- readxl::read_excel(params$ruta_equi, sheet="pba_seccprov")
head(equi)# A tibble: 6 × 2
municipio_id seccion_electoral
<dbl> <chr>
1 6854 VII
2 6588 IV
3 6007 VI
4 6014 VI
5 6021 IV
6 6028 III
Tenemos un problema de compatibilidad de tipos. En este caso, nos conviene utilizar números.
# df %>% left_join(equi, by=c("cde"="municipio_id"))Transformamos y unimos.
df <- df %>%
mutate(municipio_id = as.numeric(cde))%>%
left_join(equi, by="municipio_id")Con patchwork podemos unir ambos mapas en un mismo eje.
library(patchwork)
gba_bbox <- df %>% filter(seccion_electoral %in% c("I","III")) %>% st_bbox()
zoom_plot <- df %>%
filter(Partido == "UNION POR LA PATRIA") %>%
ggplot()+
geom_sf(aes(fill=Porcentaje), color="black")+
coord_sf(
xlim = c(gba_bbox["xmin"], gba_bbox["xmax"]),
ylim = c(gba_bbox["ymin"], gba_bbox["ymax"]),
expand = FALSE)+
scale_fill_fermenter(palette = "Blues", direction = 1, n.breaks=5,
labels = scales::label_number(suffix = "%"))+
labs(title="GBA")+
guides(fill="none")+
theme_void()
main_plot + inset_element(zoom_plot, left = .92, bottom = .52, right = 1.6, top = 1.2) Instalamos ggspatial para agregar mapas base.
#install.packages("ggspatial")
#install.packages("prettymapr")
library(ggspatial)El argumento annotation_* nos va a permitir agregar ciertos elementos al gráfico de ggplot.
df %>%
filter(Partido == "UNION POR LA PATRIA") %>%
ggplot()+
annotation_map_tile(zoom=5) +
annotation_north_arrow(location = "br", which_north = "true",
height = unit(.8, "cm"), width = unit(.8, "cm"))+
geom_sf(aes(fill=Porcentaje), color="black", alpha=.8)+
scale_fill_fermenter(palette = "Blues", direction = 1, n.breaks=5,
labels = scales::label_number(suffix = "%"))+
labs(x="", y="", fill="",
title="Unión por la Patria",
subtitle="Agregamos mapa base con librería ggspatial",
caption="Elaboración propia según datos de datacp.ar")+
theme_void()+
theme(plot.caption = element_text(hjust = 0))El argumento zoom nos permite trabajar sobre la calidad del mapa que presentamos. Un número mayor implica más tiempo de procesamiento.
df %>%
filter(Partido == "UNION POR LA PATRIA") %>%
filter(seccion_electoral %in% c("I", "III", "VIII")) %>%
ggplot()+
annotation_map_tile(zoom=7) + # con zoom=8 o más se rompe el renderizado para web. Probar localmente.
geom_sf(aes(fill=Porcentaje), color="black", alpha=.8)+
scale_fill_fermenter(palette = "Blues", direction = 1, n.breaks=3,
labels = scales::label_number(suffix = "%"))+
labs(x="", y="", fill="",
title="Unión por la Patria",
subtitle="Agregamos mapa base con librería ggspatial",
caption="Elaboración propia según datos de datacp.ar")+
theme_void()+
theme(plot.caption = element_text(hjust = 0))Podríamos probar otros mapas base.
print(rosm::osm.types()) [1] "osm" "opencycle" "hotstyle"
[4] "loviniahike" "loviniacycle" "stamenbw"
[7] "stamenwatercolor" "osmtransport" "thunderforestlandscape"
[10] "thunderforestoutdoors" "cartodark" "cartolight"
df %>%
filter(Partido == "UNION POR LA PATRIA") %>%
filter(seccion_electoral %in% c("I", "III", "VIII")) %>%
ggplot()+
annotation_map_tile(type="cartodark", zoom=7) + # con zoom=8 o más se rompe el renderizado para web. Probar localmente.
geom_sf(aes(fill=Porcentaje), color="black", alpha=.8)+
scale_fill_fermenter(palette = "Blues", direction = 1, n.breaks=3,
labels = scales::label_number(suffix = "%"))+
labs(x="", y="", fill="",
title="Unión por la Patria",
subtitle="Agregamos mapa base con librería ggspatial",
caption="Elaboración propia según datos de datacp.ar")+
theme_void()+
theme(plot.caption = element_text(hjust = 0))leafletAnte todo, se instala y carga la librería. leaflet es una librería basada en JavaScript y es ampliamente utilizada en distintos lenguajes de programación para producir mapas interactivos.
#install.packages("leaflet")
library(leaflet)Calculemos el centroide de Buenos Aires.
sf_use_s2(FALSE)
centroide <- df %>%
st_make_valid() %>%
mutate(prov="PBA") %>%
group_by(prov) %>%
summarise(geometry = st_union(geometry)) %>%
st_centroid() %>%
st_coordinates()
centroide X Y
[1,] -60.57121 -36.69671
leaflet sostiene la sintaxis de ggplot. Facilita su uso.
leaflet() %>%
addTiles() %>%
setView(lng = centroide[1], lat = centroide[2], zoom=6)También permite trabajar con dataframes.
municipios <- c(6574, 6357, 6056, 6490)
df_filtrado <- df %>% filter(Partido=="UNION POR LA PATRIA") %>% filter(municipio_id %in% municipios) %>% st_centroid()
m <- leaflet(df_filtrado) %>%
addTiles() %>%
addCircleMarkers(radius=~Porcentaje)
mTambién se puede trabajar con polígonos.
df_filtrado <- df %>% filter(Partido=="UNION POR LA PATRIA")
m <- leaflet(df_filtrado) %>%
addTiles() %>%
addPolygons()
mRepliquemos el mapa cloroplético.
# paleta de colores
pal <- colorBin("Blues", domain = df_filtrado$Porcentaje, bins = 3)
# construimos labels
df_filtrado <- df_filtrado %>%
mutate(label = paste0("<strong>", Seccion, "</strong>", "<br>", round(Porcentaje,1), "%"))
m <- leaflet(df_filtrado) %>%
addTiles() %>%
addPolygons(
fillColor = ~pal(Porcentaje),
weight=1, # ancho de bordes
opacity=.5, # transparencia de bordes
color="black", # color de bordes
fillOpacity = .6, # transparencia de área
label=~lapply(label, htmltools::HTML)
) %>%
addLegend(pal=pal,
values=~Porcentaje,
opacity=1) %>%
addControl(
html = "<b>Resultados electorales G2023 por sección</b>",
position = "topleft"
)
mElegir y resolver alguna de las siguientes consignas.
Hay momentos donde, para comprender la información, es necesario sumar variables. En el mundo electoral para entender resultados globales (a nivel provincia en este caso) siempre es útil ver Votos en absolutos y/o electores por cada departamento.
plot_porcentaje <- df_filtrado %>%
ggplot()+
geom_sf(aes(fill=Porcentaje), color="black", alpha=.8)+
scale_fill_fermenter(palette = "Blues", direction = 1, n.breaks=3,
labels = scales::label_number(suffix = "%"))+
labs(x="", y="", fill="", title="Unión por la Patria", subtitle="Porcentaje")+
theme_void()
plot_votos <- df_filtrado %>%
ggplot()+
geom_sf(aes(fill=Votos), color="black", alpha=.8)+
scale_fill_fermenter(palette = "Reds", direction = 1, n.breaks=3,
labels = scales::label_number(scale = 1/1000, suffix = "k"))+
labs(x="", y="", fill="", title="Unión por la Patria", subtitle="Votos")+
theme_void()
plot_porcentaje + plot_votosVisualmente podría representarse ambas variables a través de los mapas bivariados.
#install.packages("biscale")
#install.packages("cowplot")
library(biscale)
dim <- 2
pal <- "GrPink" # otras paletas: "Bluegill", "BlueGold", "BlueOr", "BlueYl", "Brown"/"Brown2", "DkBlue"/"DkBlue2", "DkCyan"/"DkCyan2", "DkViolet"/"DkViolet2", "GrPink"/"GrPink2", "PinkGrn", "PurpleGrn", or "PurpleOr"
df_biscale <- df_filtrado %>%
mutate(Votos = as.numeric(Votos)) %>%
bi_class(x="Votos", y="Porcentaje", style="quantile", dim=dim)
map <- ggplot(df_biscale) +
geom_sf(aes(fill = bi_class),
color = "black",
show.legend = FALSE) +
bi_scale_fill(pal = pal, dim = dim) +
labs(
title = "Unión por la Patria G2023",
# subtitle = "% y votos - mapa bivariado",
) +
bi_theme()+
theme(plot.title = element_text(size = 12), # título más chico
plot.subtitle = element_text(size = 10), # subtítulo más chico
plot.title.position = "plot"
)
legend <- bi_legend(pal = pal,
dim = dim,
xlab = "Más votos",
ylab = "Mayor %",
size = 8)
map + inset_element(legend, left = .9, bottom = -.7, right = 1.6, top = 1.2) Último tema para este encuentro. Al momento de diseñar nuestra visualización es importante tener a mano los distintos atributos de los que se puede hacer uso. Los puntos son el objeto geográfico que nos permite jugar más con los distintos atributos. Para ayudar a entender la importancia de ellos, se utilizarán los resultados de la elección presidencial del año 2015 en la Provincia de Buenos Aires.
df15 <- readxl::read_excel(params$ruta_pba_g15) %>%
left_join(geo, by="id") %>%
mutate(Porcentaje = as.numeric(Porcentaje),
Votos = as.numeric(Votos)) %>%
st_as_sf() %>%
group_by(id) %>%
mutate(
ganador = Partido[which.max(Porcentaje)]
) %>%
ungroup()
dim(df15)[1] 1215 14
Una mínima limpieza. Construir la variable de ganador, pasar la geometría a puntos y agregar las coordenadas como variables.
#colores de partidos
colores <- c( "FRENTE PARA LA VICTORIA" = "skyblue",
"CAMBIEMOS" = "orange",
"UNIDOS POR UNA NUEVA ALTERNATIVA (UNA)" = "deeppink" )
df15 %>%
ggplot()+
geom_sf(aes(fill=ganador))+
scale_fill_manual(values=colores, name="ganador")+
labs(title="Ganador por departamento",
subtitle="Elecciones presidenciales G2015")+
theme_void()Existe un efecto visual dado por la relación entre las áreas de los polígonos y los ganadores: parece que CAMBIEMOS obtuvo más votos.
df15 %>%
st_drop_geometry() %>%
group_by(Partido) %>%
summarise(votos=sum(as.numeric(Votos))) %>%
arrange(desc(votos))# A tibble: 9 × 2
Partido votos
<chr> <dbl>
1 FRENTE PARA LA VICTORIA 3419041
2 CAMBIEMOS 3031168
3 UNIDOS POR UNA NUEVA ALTERNATIVA (UNA) 2062610
4 FRENTE DE IZQUIERDA Y DE LOS TRABAJADORES 341734
5 PROGRESISTAS 264583
6 BLANCO 223902
7 COMPROMISO FEDERAL 89256
8 NULO 55030
9 IMPUGNADO 7400
df15 %>%
st_drop_geometry() %>%
distinct(id, ganador) %>%
count(ganador) %>%
arrange(desc(n))# A tibble: 3 × 2
ganador n
<chr> <int>
1 CAMBIEMOS 83
2 FRENTE PARA LA VICTORIA 50
3 UNIDOS POR UNA NUEVA ALTERNATIVA (UNA) 2
Veamos con puntos al ganador por municipio utilizando puntos.
# transformamos la geometría a puntos
df_point <- df15 %>%
st_centroid() %>%
mutate(
X = st_coordinates(.)[,1],
Y = st_coordinates(.)[,2]
)
# creamos una capa por sección provincial para darle un marco
geo_prov <- st_read(params$ruta_shp) %>%
filter(NAM == "Buenos Aires")Reading layer `ign_provincia' from data source
`C:\Users\tomas.bustos\Documents\personal\estacion-r\Mapas en accion\geo\ign_provincia\ign_provincia.shp'
using driver `ESRI Shapefile'
Simple feature collection with 24 features and 11 fields
Geometry type: MULTIPOLYGON
Dimension: XY
Bounding box: xmin: -74 ymin: -90 xmax: -25 ymax: -21.78086
Geodetic CRS: WGS 84
#print(rosm::osm.types())
df_point %>%
ggplot()+
#annotation_map_tile(zoom=5, type="cartolight")+
geom_sf(aes(color=ganador))+
scale_color_manual(values=colores, name="ganador")+
geom_sf(data=geo_prov, alpha=0)+
labs(title="Ganador por departamento",
subtitle="Elecciones presidenciales G2015")+
theme_void()Se puede jugar con el color, el tamaño y la intensidad.
df_point %>%
ggplot()+
geom_sf(aes(fill = ganador, size = Porcentaje),
shape = 21, color = "black", alpha = 0.8) +
scale_fill_manual(values=colores, name="ganador")+
geom_sf(data=geo_prov, alpha=0)+
labs(title="Ganador por departamento",
subtitle="Elecciones presidenciales G2015")+
theme_void()